#include <iostream>
#include <string>

#include <cstddef>
#include <cstdlib>
#include <cstring>

#include <mcheck.h>
#include <unistd.h>

void *malloc_hook(std::size_t size,const char *file, int line) {

    void *ptr=malloc(size);
    if(nullptr==ptr) {
        return nullptr;
    }

    // 生成文件名,以申请内存的首地址命名
    char buffer[128]{'\0'};
    sprintf(buffer,"./memory_file/%p.mem",ptr);

    // 打开文件
    FILE *pf=fopen(buffer,"w");

    // 将申请内存的信息写入文件中
    fprintf(pf,"[%s : %d] ---> addr:%p size: %u\n",file,line,ptr,size);
    fflush(pf);

    // 关闭文件
    fclose(pf);

    return ptr;
}

void free_hook(void *ptr,const char *file, int line) {

    if(nullptr==ptr) {
        return;
    }

    // 生成文件名,以申请内存的首地址命名
    char buffer[128]{'\0'};

    sprintf(buffer,"./memory_file/%p.mem",ptr);

    if(-1 == unlink(buffer)) {
        fprintf(stderr,"double free ptr:%p\n",ptr);

        return;
    }   

    free(ptr);
}

// #define malloc(size) malloc_hook(size,__FILE__,__LINE__)
// #define free(ptr) free_hook(ptr,__FILE__,__LINE__)

void test_hook() {

    int *ptr1=(int *)malloc(sizeof(int)*5);
    if(nullptr==ptr1) {
        perror("malloc");
        return;
    }

    int *ptr2=(int *)malloc(sizeof(int)*5);
    if(nullptr==ptr2) {
        perror("malloc");
        return;
    }

    ptr1=ptr2;

    free(ptr1);
    // free(ptr2);
}

// void test_builtin() {
//     void *ptr=__builtin_return_address(1);

//     std::cout<<ptr<<std::endl;
// }

void test_const() {

    // 3. volatile保持内存可见性
    volatile int const val3=100;

    // const_cast去除const属性
    int *p3=const_cast<int *>(&val3);

    *p3=10;

    std::cout<<*p3<<std::endl;
    // 因为val3是常量所以编译器会进行优化, 将val3的值一直保存在CPU寄存器中
    // 所以如果对内存中的val3的值进行修改, 不会影响CPU寄存器中的值(编译器认为是常量,不会对val3进行写入也即
    // 值不会改变,就把它存入CPU寄存器中省的每次读取都要从内存中读取到寄存器中,降低效率.
    // 所以每次读取val3的时候都向寄存器中读取)
    // 虽然通过const_cast+int *p3改变了内存中val3的值,但在访问val3的时候还是向寄存器中读取,它并不知道val3内存值改变了
    // 因为以为他是常量只能读. 所以*p3输出10, val3输出100. 
    // 通过增加volatile其作用是告诉CPU每次读取val3时,都需要从内存中读到寄存器中在进行往后的计算
    // 进而保证了val3的内存可见性
    std::cout<<val3<<std::endl; 

    /*
        $ ./dynamic_memory // 没有加volatile
        10
        100

        $ make 
        g++ -o dynamic_memory dynamic_memory.cc -std=c++11
        $ ./dynamic_memory // 加volatile
        10
        10
    */

    // 2. err
    // int const val2=100;
    
    // const int *p2=&val2;

    // *p2=10;

    // std::cout<<*p2<<std::endl;
    // std::cout<<val2<<std::endl;

    // 1. err
    // int const val1=100;

    // int *p1=&val1;

    // *p1=10;

    // std::cout<<*p1<<std::endl;
    // std::cout<<val1<<std::endl;
}

void test_copy_on_write() {

    std::string str1="hello string";
    std::string str2=str1;

    printf ("Sharing the memory:\n");
    printf("\tstr1: %p\n",str1.c_str());
    printf("\tstr2: %p\n",str2.c_str());

    str1[0]='H';
    str2[6]='S';

    printf ("After Copy-On-Write:\n");
    printf("\tstr1: %p\n",str1.c_str());
    printf("\tstr2: %p\n",str2.c_str());
}

void test_mtrace() {

    mtrace(); // 安装hook

    int *p=(int *)malloc(sizeof(int) * 10);

    for(int i=0;i<10;++i)   p[i]=i;

    free(p);
    p=nullptr;

    p=(int *)malloc(sizeof(int) * 5);

    muntrace(); // 取消hook

    // export MALLOC_TRACE=./trace.log
    /*
        [yx@VM-12-12-centos dynamic_memory_management]$ cat trace.log 
        = Start
        @ ./dynamic_memory:[0x40127a] + 0x676070 0x28
        @ ./dynamic_memory:[0x4012b6] - 0x676070
        @ ./dynamic_memory:[0x4012cb] + 0x6760a0 0x14
        = End
    */

}

int main() {

    // test_hook();
    // test_const();
    // test_copy_on_write();

    test_mtrace();

    //printf("%p\n",main);
    //std::cout<<&main<<std::endl;
    //test_builtin();

    return 0;
}